package com.hsd.whb.utils;

import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/*
 * @auther: wangjiacai
 *
 * @create: 2018-09-08 19:32:32
 */
public class BlockInputStream extends InputStream {

    private volatile boolean finish = false;

    private volatile boolean blocking = true;

    protected int max_size = 100000;

    protected byte[] buf;

    protected int readPos;

    protected int writePos;

    protected int mark = 0;

    protected int count;


    public BlockInputStream() {
        this.buf = new byte[max_size];
        this.readPos = 0;
        this.writePos = 0;
        this.count = buf.length;
    }

    public BlockInputStream(int size) {
        this.max_size = size;
        this.buf = new byte[max_size];
        this.readPos = 0;
        this.writePos = 0;
        this.count = buf.length;
    }

    public BlockInputStream(byte buf[]) {
        this.buf = buf;
        this.readPos = 0;
        this.writePos = buf.length;
        this.count = this.writePos;
    }

    public BlockInputStream(byte buf[], int offset, int length) {
        this.buf = buf;
        this.readPos = offset;
        this.writePos = Math.min(offset + length, buf.length);
        this.count = this.writePos;
        this.mark = offset;
    }

    private void await() {
        try {
            synchronized (this) {
                this.wait();
            }
        } catch (InterruptedException e) {
        }
    }

    private boolean empty() {
        if (writePos == readPos) {
            if (blocking) {
                await();
                return false;
            } else {
                return true;
            }
        }
        return false;
    }

    @Override
    public int read(byte[] b) {
        return read(b, 0, b.length);
    }

    @Override
    public int read(byte b[], int off, int len) {
        if (b == null) {
            throw new NullPointerException();
        } else if (off < 0 || len < 0 || len > b.length - off) {
            throw new IndexOutOfBoundsException();
        }

        if (len == 0) return 0;
        if (finish && writePos == readPos) return -1;
        if (empty()) return 0;
        if (finish && writePos == readPos) return -1;


        int avail = writePos - readPos;
        if (avail < 0) avail = avail + max_size;
        if (len > avail) {
            len = avail;
        }

        for (int i = 0; i < b.length; i++) {
            b[i] = 0;
        }
        for (int i = off; i < len; i++) {
            b[i] = buf[readPos++];
            if (readPos == max_size) readPos = 0;
        }
        return len;
    }

    @Override
    public int read() {
        if (finish && (writePos == readPos)) return -1;
        if (empty()) return 0;
        int data = buf[readPos++];
        if (readPos == max_size) readPos = 0;
        return data;
    }

    public long skip(long n) {
        long k = Math.abs(writePos - readPos);
        if (n < k) {
            k = n < 0 ? 0 : n;
        }
        readPos = (int) (readPos + k) % max_size;
        return k;
    }

    public int available() {
        return Math.abs(writePos - readPos);
    }

    public boolean markSupported() {
        return true;
    }

    public void mark(int readAheadLimit) {
        mark = readPos;
    }


    public void reset() {
        readPos = mark;
    }

    public void close() {
        buf = null;
    }

    public void write(byte a) {
        buf[writePos++] = a;
        if (writePos == max_size) writePos = 0;
        synchronized (this) {
            this.notifyAll();
        }
    }

    public void write(byte[] bytes) {
        for (byte b : bytes) {
            buf[writePos++] = b;
            if (writePos == max_size) writePos = 0;
        }
        synchronized (this) {
            this.notifyAll();
        }
    }

    public void write(byte[] b, int off, int len) {
        int length = Math.min(b.length, off + len);
        for (int i = off; i < length; i++) {
            buf[writePos++] = b[i];
            if (writePos == max_size) writePos = 0;
        }
        synchronized (this) {
            this.notifyAll();
        }
    }

    public void write(int a) {
        for (byte b : IntToByte(a)) {
            buf[writePos++] = b;
            if (writePos == max_size) writePos = 0;
        }
        synchronized (this) {
            this.notifyAll();
        }
    }

    public void setFinish(boolean finish) {
        this.finish = finish;
        synchronized (this) {
            this.notifyAll();
        }
    }

    /**
     * int转byte数组
     *
     * @param num
     * @return
     */
    public byte[] IntToByte(int num) {
        byte[] bytes;
        int length;
        if (num < Math.pow(2, 8)) {
            length = 1;
            bytes = new byte[length];
        } else if (num < Math.pow(2, 16)) {
            length = 2;
            bytes = new byte[length];
        } else if (num < Math.pow(2, 24)) {
            length = 3;
            bytes = new byte[length];
        } else {
            length = 4;
            bytes = new byte[length];
        }
        for (int i = 0; i < length; i++) {
            bytes[i] = (byte) ((num >> ((length - i - 1) * 8)) & 0xff);
        }
        return bytes;
    }

}
